/**
 * 建模应用
 */
import tnx from '@/tnx.js';
import TypeKind from '../enums/TypeKind.js';
import BaseType from './BaseType.js';
import EnumClass from './EnumClass.js';
import StandardClass from './StandardClass.js';
import EntityClass from './EntityClass.js';
import ValueClass from './ValueClass.js';
import PlainClass from './PlainClass.js';
import TableNameRule from './TableNameRule.js';
import DependencyLib from './DependencyLib.js';
import DependencyLibType from '../enums/DependencyLibType.js';
import InheritanceStrategy from '../enums/InheritanceStrategy.js';

export default class ModelingApp {

    path = '';
    name = '';
    caption = '';
    /**
     * 存储路径
     * @type {string}
     */
    databaseVendor = '';
    tableNameRule = new TableNameRule();
    /**
     * @type {[ModelingClass]}
     */
    classes = [];
    /**
     *
     * @type {DependencyLib[]}
     */
    dependencies = [];
    dependencyInitializing = false;
    /**
     * @type {Date}
     */
    saveTime = null;

    constructor(name, caption) {
        this.name = name;
        this.caption = caption || name;
    }

    static of(object) {
        if (typeof object !== 'object') {
            return undefined;
        }
        let app = new ModelingApp(this.name, this.caption);
        app.update(object);
        return app;
    }

    static ofClass(classObject, kindScene) {
        let kind = classObject.kind;
        if (typeof kind === 'string') {
            kind = TypeKind.validate(kind, kindScene || 'modeling');
        }
        if (kind instanceof TypeKind) {
            if (TypeKind.ENUM.equals(kind)) {
                return EnumClass.of(classObject);
            }
            if (kind.supports('entity')) {
                return EntityClass.of(classObject);
            }
            if (TypeKind.VALUE.equals(kind)) {
                return ValueClass.of(classObject);
            }
            if (TypeKind.PLAIN.equals(kind)) {
                return PlainClass.of(classObject);
            }
            return BaseType.of(classObject);
        }
        return undefined;
    }

    update(object) {
        tnx.util.object.rewriteSimpleDefined(this, object);
        if (typeof object === 'object') {
            if (object.tableNameRule) {
                this.tableNameRule = new TableNameRule().update(object.tableNameRule);
            }
            if (object.classes) {
                this.classes = object.classes.map(ModelingApp.ofClass).filter(c => c !== undefined);
            }
            this.sortClasses();
            if (object.dependencies) {
                this.dependencies = object.dependencies.map(d => DependencyLib.of(d));
            }
        }
        return this;
    }

    clone() {
        return ModelingApp.of(this);
    }

    sortClasses() {
        this.classes.sort((c1, c2) => {
            let order = c1.packageName.localeCompare(c2.packageName);
            if (order === 0) {
                order = c1.simpleName.localeCompare(c2.simpleName);
            }
            return order;
        });
    }

    addClass(clazz) {
        this.classes = this.classes || [];
        this.classes.push(clazz);
        this.sortClasses();
    }

    findClass(clazz, findInDependencies = false, toOrigin = false) {
        if (clazz) {
            let result = this.classes.find(c => c.matches(clazz));
            if (!result && findInDependencies) {
                for (let dependency of this.dependencies) {
                    result = dependency.findClass(clazz);
                    if (result) {
                        return toOrigin ? result.origin : result;
                    }
                }
            }
            return result;
        }
        return null;
    }

    getClassName(classId, simple) {
        if (classId) {
            let clazz = this.findClass(classId, true, true);
            if (clazz) {
                return simple ? clazz.simpleName : clazz.getName();
            }
        }
        return classId;
    }

    removeClass(clazz) {
        return this.classes.remove(c => c.matches(clazz));
    }

    updateClass(id, model) {
        let clazz = this.classes.find(c => c.id === id);
        if (clazz) {
            if (model.kind && model.kind !== clazz.kind.name) { // 如果性质有变更，重建新类替换原类
                clazz = this.rebuildClass(id, model);
            } else {
                clazz.update(model);
                if (clazz instanceof EntityClass) {
                    clazz.ensurePropertyId();
                }
            }
        }
        return clazz;
    }

    rebuildClass(classId, model) {
        let index = this.classes.findIndex(c => c.id === classId);
        if (index >= 0) {
            let newClass = ModelingApp.ofClass(model, 'modeling');
            newClass.id = classId;
            this.classes[index] = newClass;
            return newClass;
        }
        return null;
    }

    /**
     * 同步指定父类的所有各级子类的性质
     * @param superClass 父类
     * @return {boolean} 是否有变更
     */
    syncSubClasses(superClass) {
        let result = false;
        // 只有实体类才需要同步子类的性质
        if (superClass instanceof EntityClass) {
            this.loopSubClasses(superClass, subClass => {
                // 性质与父类不同，则重建新类替换原类
                if (!subClass.kind.equals(superClass.kind)) {
                    this.rebuildClass(subClass.id, Object.assign({}, subClass, {
                        kind: superClass.kind,
                    }));
                    result = true;
                }
            });
        }
        return result;
    }

    loopSubClasses(superClass, consumer) {
        if (this.classes) {
            for (let clazz of this.classes) {
                if (clazz instanceof StandardClass && clazz.superClassId === superClass.id) {
                    consumer(clazz);
                    this.loopSubClasses(clazz, consumer);
                }
            }
        }
    }

    isExtended(superClassId, subClassId) {
        let subClass = this.findClass(subClassId, true, true);
        if (subClass instanceof StandardClass) {
            if (subClass.superClassId === superClassId) {
                return true;
            }
            if (subClass.superClassId) {
                return this.isExtended(superClassId, subClass.superClassId);
            }
        }
        return false;
    }

    queryClass(keyword) {
        let classes = [];
        keyword = keyword ? keyword.trim() : '';
        if (keyword) {
            for (let clazz of this.classes) {
                if (clazz.getName().toLowerCase().includes(keyword.toLowerCase())) {
                    classes.push(clazz);
                }
            }
            for (let dependency of this.dependencies) {
                classes.push(...dependency.queryClass(keyword));
            }
        }
        return classes;
    }

    getBasePackageName() {
        let packages = [];
        for (let clazz of this.classes) {
            if (clazz.packageName && !packages.includes(clazz.packageName)) {
                packages.push(clazz.packageName);
            }
        }

        if (packages.length === 0) {
            return '';
        }

        if (packages.length === 1) {
            return packages[0];
        }

        // 将所有非空包名按照'.'分割成数组
        let packageLists = packages.map(packageName => packageName.split('.'));

        // 初始化公共前缀数组
        let commonPrefix = [];

        // 获取最短包名的长度，避免越界
        let minLength = Math.min(...packageLists.map(list => list.length));

        // 逐层比较
        for (let i = 0; i < minLength; i++) {
            // 取出当前层级的所有元素
            let levelElements = packageLists.map(list => list[i]);

            // 检查当前层级的所有元素是否相同
            if (new Set(levelElements).size === 1) {
                // 如果相同，则添加到公共前缀
                commonPrefix.push(levelElements[0]);
            } else {
                // 如果遇到不同的层级，则停止
                break;
            }
        }

        // 使用'.'连接公共前缀数组中的元素以形成完整的包名
        return commonPrefix.join('.');
    }

    sortDependencies() {
        this.dependencies.sort((d1, d2) => {
            let order1 = d1.type.ordinal;
            let order2 = d2.type.ordinal;
            if (order1 === order2) {
                return d1.getName().localeCompare(d2.getName());
            } else {
                return order1 - order2;
            }
        });
    }

    needsSyncDependencies() {
        return this.dependencies.some(
            dependency => DependencyLibType.MODELING.equals(dependency.type) && dependency.needsSync());
    }

    syncReferences(reset) {
        if (reset) { // 先清空所有被引用次数
            for (let dependency of this.dependencies) {
                for (let clazz of dependency.classes) {
                    clazz.referencedNum = 0;
                }
            }
        }
        // 统计被引用次数
        for (let clazz of this.classes) {
            if (clazz.properties) {
                for (let property of clazz.properties) {
                    for (let dependency of this.dependencies) {
                        // 依赖库是同工程下的其它模块的类才需要统计
                        if (DependencyLibType.MODELING.equals(dependency.type)) {
                            let dependencyClass = dependency.findClass(property.typeId);
                            if (dependencyClass) {
                                // 建模类才需要统计被引用次数
                                if (dependencyClass.getKind().supports('modeling')) {
                                    dependencyClass.referencedNum++;
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }
    }

    existsPrimaryProperty(clazz) {
        clazz = this.findClass(clazz, true, true);
        // 只有实体才会有主键属性，且实体的子类一定也是同性质的实体，所以一旦类或父类不是实体，则可以断定没有主键属性
        while (clazz instanceof EntityClass) {
            if (clazz.existsPrimaryProperty()) {
                return true;
            }
            clazz = this.findClass(clazz.superClassId, true, true);
        }
        return false;
    }

    getSuperClassWithInheritanceStrategy(clazz) {
        let superClass = this.findClass(clazz.superClassId, true, true);
        // 父类存在，没有指定继承映射策略，而且还有更上层父类，则继续取上层父类
        while (superClass && !superClass.inheritanceStrategy && superClass.superClassId) {
            superClass = this.findClass(superClass.superClassId, true, true);
        }
        if (superClass && !superClass.inheritanceStrategy) {
            superClass = null;
        }
        return superClass;
    }

    getTableName(clazz) {
        let tableName = clazz.tableName;
        if (!tableName) {
            let superClassWithInheritanceStrategy = this.getSuperClassWithInheritanceStrategy(clazz);
            if (superClassWithInheritanceStrategy
                && InheritanceStrategy.SINGLE_TABLE.equals(superClassWithInheritanceStrategy.inheritanceStrategy)) {
                tableName = this.tableNameRule.getDefaultTableName(superClassWithInheritanceStrategy);
            }
            if (!tableName) {
                tableName = this.tableNameRule.getDefaultTableName(clazz);
            }
        }
        return tableName;
    }

    getPrimaryLogicTypeIds(clazz, toPrimitive) {
        let typeIds = [];
        if (clazz) {
            clazz = this.findClass(clazz, true, true);
            if (clazz && typeof clazz.getPrimaryLogicTypeIds === 'function') {
                typeIds = clazz.getPrimaryLogicTypeIds(toPrimitive);
                while (!typeIds.length && clazz.superClassId) {
                    clazz = this.findClass(clazz.superClassId, true, true);
                    if (clazz) {
                        typeIds = clazz.getPrimaryLogicTypeIds(toPrimitive);
                    } else {
                        break;
                    }
                }
            }
        }
        return typeIds;
    }

    getPrimaryLogicNames(clazz) {
        let names = [];
        if (clazz) {
            clazz = this.findClass(clazz, true, true);
            if (clazz) {
                names = clazz.getPrimaryLogicNames();
                while (!names.length && clazz.superClassId) {
                    clazz = this.findClass(clazz.superClassId, true, true);
                    if (clazz) {
                        names = clazz.getPrimaryLogicNames();
                    } else {
                        break;
                    }
                }
            }
        }
        return names;
    }

    /**
     * 获取指定集合属性的元素的引用类型
     * @param property 集合属性
     * @return {*|ModelingClass|null} 指定集合属性的元素的引用类型，找不到则返回null
     */
    getCollectionElementRefClass(property) {
        // 不从util下引入静态变量，util的层级更高，不应该反向依赖
        if (['java.util.List', 'java.util.Set', 'java.util.Collection'].includes(property.typeId)) {
            let genericType = property.genericTypes[0];
            if (genericType?.typeId) {
                let refClass = this.findClass(genericType.typeId, true, true);
                if (refClass && refClass.kind.supports('association')) {
                    return refClass;
                }
            }
        }
        return null;
    }

}
